﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using FC.GEPluginCtrls;
using FC.GEPluginCtrls.Geo;

namespace VehicleTrajectoryApp
{
    public class GPSLink
    {
        public float x1;
        public float y1;
        public float x2;
        public float y2;

        public GPSLink(float x1, float y1, float x2, float y2)
        {
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
        }
    }

    public class GPSTrajectory : IVehicle
    {
        int ID;
        int origin_zone_id;
        int dest_zone_id;
        Int64 initial_time;
        float travel_time;
        float start_time;
        float travel_distance_in_mile = 0;
        VehicleTrajectoryType type = VehicleTrajectoryType.GPS;

        public int VehicleID
        {
            get
            {
                return ID;
            }
        }

        public VehicleTrajectoryType TrajectoryType
        {
            get
            {
                return type;
            }
        }

        public int OriginZoneID
        {
            get
            {
                return origin_zone_id;
            }
        }

        public int DestinationZoneID
        {
            get
            {
                return dest_zone_id;
            }
        }

        public DateTime VehicleStartTime
        {
            get
            {
                return new DateTime(initial_time);
            }
        }

        public int VehicleStartHour
        {
            get
            {
                int start_hour = ((int)start_time / 60);
                return start_hour;
            }
        }
        public float VehicleTravelTime
        {
            get
            {
                return travel_time;
            }
        }

        public List<int> NodeList
        {
            get
            {
                return null;
            }
        }
        public float Travel_Distance_In_Mile
        {
            get
            {
                return travel_distance_in_mile;
            }
        }
        int num_of_link;
        GPSLink[] gpsLinkArray;

        public GPSTrajectory(int trajID, int o_ID, int d_ID, Int64 initial_time, float start_time, float travel_time, int num_of_link)
        {
            this.ID = trajID;
            this.origin_zone_id = o_ID;
            this.dest_zone_id = d_ID;
            this.initial_time = initial_time;
            this.start_time = start_time;
            this.travel_time = travel_time;
            this.num_of_link = num_of_link;
            gpsLinkArray = new GPSLink[num_of_link];
        }

        public void SetGPSLinkArray(GPSLink[] gpsLinkArray)
        {
            this.gpsLinkArray = gpsLinkArray;
        }

        public GPSLink[] GetGPSLinkArray()
        {
            return this.gpsLinkArray;
        }

        public string ConvertVehicleTraceToPlacemark(string name, string colorCode = "ffff0000")
        {
            //string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
            //s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
            //s += "<Document>";
            //s += "<name>" + name + "</name>\n";
            string s = "<Placemark>\n";
            s += "<name>" + name + "</name>\n";
            s += "<Style><LineStyle><width>4</width><color>";
            //s += "ffff0000";
            s += colorCode;
            s += "</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>\n";
            s += "<LineString>\n";
            s += "<extrude>0</extrude>\n";
            s += "<tessellate>0</tessellate>\n";
            s += "<altitudeMode>relativeToGround</altitudeMode>\n";
            s += "<coordinates>\n";

            for (int n = 0; n < num_of_link; n++)
            {
                s += String.Format("{0},{1} {2},{3} ", gpsLinkArray[n].x1, gpsLinkArray[n].y1, gpsLinkArray[n].x2, gpsLinkArray[n].y2);
            }

            s += "</coordinates>\n";
            s += "</LineString>\n";
            s += "</Placemark>\n";
            //s += "</Document>";
            //s += "</kml>";

            return s;
        }

        public string ConvertVehicleTraceToKMLString(string name, string colorCode = "ffff0000")
        {
            string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
            s += "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
            s += "<Document>";
            s += "<name>" + name + "</name>\n";
            s += "<Placemark>\n";
            s += "<name>" + name + "</name>\n";
            s += "<Style><LineStyle><width>4</width><gx:labelVisibility>1</gx:labelVisibility><color>";
            //s += "ffff0000";
            s += colorCode;
            s += "</color></LineStyle><IconStyle><scale>0</scale></IconStyle><PolyStyle><fill>0</fill></PolyStyle></Style>\n";
            s += "<MultiGeometry>";
            s += "<LineString>\n";
            s += "<extrude>0</extrude>\n";
            s += "<tessellate>0</tessellate>\n";
            s += "<altitudeMode>relativeToGround</altitudeMode>\n";
            s += "<coordinates>\n";

            for (int n = 0; n < num_of_link; n++)
            {
                s += String.Format("{0},{1} {2},{3} ", gpsLinkArray[n].x1, gpsLinkArray[n].y1, gpsLinkArray[n].x2, gpsLinkArray[n].y2);
            }

            s += "</coordinates>\n";
            s += "</LineString>\n";
            s += "<Point>";
            s += "<coordinates>";
            s += String.Format("{0},{1},0\n", gpsLinkArray[0].x1, gpsLinkArray[0].y1);
            s += "</coordinates>";
            s += "</Point>\n";
            s += "</MultiGeometry>\n";
            s += "</Placemark>\n";
            s += "</Document>";
            s += "</kml>";

            return s;
        }

        public static List<IVehicle> ReadBinaryGPSTrajectory2(string binaryFileName, VehicleTrajectoryType trajectory_type, int start_hour = 6, int end_hour = 12)
        {
            List<IVehicle> gpsTrajectoryList = new List<IVehicle>();
            BinaryReader binReader = new BinaryReader(File.Open(binaryFileName, FileMode.Open));
            
            int original_tomtom_vehicle_id;
            int date_id;
            int vehicle_id;
            int origin_zone;
            int dest_zone;
            float departure_time_in_min;
            float arrival_time_in_min;
            float trip_time_in_min;
            int num_of_nodes;
            float start_x;
            float start_y;
            float end_x;
            float end_y;
            float link_start_time;
            float distance;

            //string tmpFileName = System.IO.Path.GetDirectoryName(binaryFileName) + "\\" + System.IO.Path.GetFileNameWithoutExtension(binaryFileName) + "-2.bin";
            //BinaryWriter binWriter = new BinaryWriter (File.Open(tmpFileName,FileMode.Create));
            try
            {
                while (true)
                {
                    original_tomtom_vehicle_id = binReader.ReadInt32();
                    date_id = binReader.ReadInt32();
                    vehicle_id = binReader.ReadInt32();
                    origin_zone = binReader.ReadInt32();
                    dest_zone = binReader.ReadInt32();
                    departure_time_in_min = binReader.ReadSingle();
                    arrival_time_in_min = binReader.ReadSingle();
                    trip_time_in_min = binReader.ReadSingle();
                    distance = binReader.ReadSingle();
                    //convert distance from kilometers to miles
                    distance *= 0.621371f;

                    num_of_nodes = binReader.ReadInt32();

                    //Three reserved float numbers
                    binReader.ReadSingle();
                    binReader.ReadSingle();
                    binReader.ReadSingle();

                    DateTime dateTime;
                    int hour, min, sec;
                    hour = (int)(departure_time_in_min / 60);
                    min = (int)(departure_time_in_min - 60 * hour);
                    sec = (int)(departure_time_in_min - 60 * hour - min) * 60;
                    if (date_id == 18)
                    {
                        dateTime = new DateTime(2012, 7, 18, hour, min, sec);
                    }
                    else
                    {
                        if (date_id == 0)
                        {
                            dateTime = new DateTime(2012, 1, 1, hour, min, sec);
                        }
                        else
                        {
                            dateTime = new DateTime(2010, 5, date_id, hour, min, sec);
                        }
                    }
                    long initial_time = dateTime.ToBinary();
                    GPSTrajectory gpsTrajectory = new GPSTrajectory(vehicle_id, origin_zone, dest_zone, initial_time, departure_time_in_min, trip_time_in_min, num_of_nodes - 1);
                    GPSLink[] gpsLinkArray = new GPSLink[num_of_nodes - 1];

                    gpsTrajectory.travel_distance_in_mile = distance;


                    if (num_of_nodes > 0)
                    {
                        start_x = binReader.ReadSingle();
                        start_y = binReader.ReadSingle();
                        link_start_time = binReader.ReadSingle();
                        distance = binReader.ReadSingle();


                        for (int n = 1; n < num_of_nodes; n++)
                        {
                            end_x = binReader.ReadSingle();
                            end_y = binReader.ReadSingle();
                            link_start_time = binReader.ReadSingle();
                            distance = binReader.ReadSingle();

                            gpsLinkArray[n - 1] = new GPSLink(start_x, start_y, end_x, end_y/*, link_start_time, link_travel_time*/);

                            start_x = end_x;
                            start_y = end_y;
                        }
                        
                        if (gpsLinkArray.Length > 0)
                        {
                            gpsTrajectory.SetGPSLinkArray(gpsLinkArray);

                            //distance = 0.0f;
                            //for (int n = 0; n < gpsLinkArray.Length; n++)
                            //{
                            //    if (Double.IsNaN(Maths.DistanceCosine(new Coordinate(longitude: gpsLinkArray[n].x1, latitude: gpsLinkArray[n].y1),
                            //        new Coordinate(longitude: gpsLinkArray[n].x2, latitude: gpsLinkArray[n].y2), UnitSystem.Imperial)))
                            //    {
                            //        //Console.WriteLine("Wrong calculation value!");
                            //    }
                            //    else
                            //    {
                            //        distance += (float)Maths.DistanceCosine(new Coordinate(longitude: gpsLinkArray[n].x1, latitude: gpsLinkArray[n].y1),
                            //        new Coordinate(longitude: gpsLinkArray[n].x2, latitude: gpsLinkArray[n].y2), UnitSystem.Imperial);
                            //    }
                            //}

                            //binWriter.Write(original_tomtom_vehicle_id);
                            //binWriter.Write(date_id);
                            //binWriter.Write(vehicle_id);
                            //binWriter.Write(origin_zone);
                            //binWriter.Write(dest_zone);
                            //binWriter.Write(departure_time_in_min);
                            //binWriter.Write(arrival_time_in_min);
                            //binWriter.Write(trip_time_in_min);
                            //binWriter.Write(distance);
                            //binWriter.Write(num_of_nodes);

                            //for (int n = 0; n < gpsLinkArray.Length; n++)
                            //{
                            //    binWriter.Write(gpsLinkArray[n].x1);
                            //    binWriter.Write(gpsLinkArray[n].y1);
                            //    binWriter.Write(0.0f);
                            //    binWriter.Write(0.0f);
                            //}
                            //binWriter.Write(gpsLinkArray[gpsLinkArray.Length - 1].x2);
                            //binWriter.Write(gpsLinkArray[gpsLinkArray.Length - 1].y2);
                            //binWriter.Write(0.0f);
                            //binWriter.Write(0.0f);


                            //gpsTrajectory.travel_distance_in_mile = distance;

                            int vehicle_start_hour = (int)(departure_time_in_min / 60);
                            if (vehicle_start_hour >= start_hour && vehicle_start_hour < end_hour && origin_zone != 0 && dest_zone != 0)
                            {
                                gpsTrajectoryList.Add(gpsTrajectory);
                            }
                        }
                    }
                }
            }
            catch (EndOfStreamException)
            {
            }
            catch
            {
                throw;
            }

            //binWriter.Close();
            binReader.Close();
            return gpsTrajectoryList;
        }

        public static List<IVehicle> ReadBinaryGPSTrajectory(string binaryFileName, int start_hour = 6, int end_hour = 12)
        {
            List<IVehicle> gpsTrajectoryList = new List<IVehicle>();
            BinaryReader binReader = new BinaryReader(File.Open(binaryFileName, FileMode.Open));

            int id;
            int origin_zone;
            int dest_zone;
            Int64 initial_time;
            float travel_time;
            float vehicle_start_time;
            int num_of_links;
            float x1;
            float y1;
            float x2;
            float y2;
            //double link_travel_time;
            //double link_start_time;

            try
            {
                while (true)
                {
                    id = binReader.ReadInt32();
                    origin_zone = binReader.ReadInt32();
                    dest_zone = binReader.ReadInt32();
                    initial_time = binReader.ReadInt64();
                    vehicle_start_time = binReader.ReadSingle();
                    travel_time = binReader.ReadSingle();

                    num_of_links = binReader.ReadInt32();
                    GPSTrajectory gpsTrajectory = new GPSTrajectory(id, origin_zone, dest_zone, initial_time, vehicle_start_time, travel_time, num_of_links);
                    GPSLink[] gpsLinkArray = new GPSLink[num_of_links];

                    for (int n = 0; n < num_of_links; n++)
                    {
                        x1 = binReader.ReadSingle();
                        y1 = binReader.ReadSingle();
                        x2 = binReader.ReadSingle();
                        y2 = binReader.ReadSingle();
                        //link_start_time = binReader.ReadDouble();
                        //link_travel_time = binReader.ReadDouble();
                        gpsLinkArray[n] = new GPSLink(x1, y1, x2, y2/*, link_start_time, link_travel_time*/);
                    }
                    gpsTrajectory.SetGPSLinkArray(gpsLinkArray);

                    int vehicle_start_hour = (int)(vehicle_start_time / 60);
                    if (vehicle_start_hour >= start_hour && vehicle_start_hour < end_hour)
                    {
                        gpsTrajectoryList.Add(gpsTrajectory);
                    }
                }
            }
            catch (EndOfStreamException)
            {
            }
            catch
            {
                throw;
            }

            binReader.Close();
            return gpsTrajectoryList;
        }

        //public static void ConvertTextGPSFileToBinaryFile(string srcTextFileFolderName, string destBinaryFileName)
        //{
        //    int n = 0;
        //    using (BinaryWriter binWriter = new BinaryWriter(File.Open(destBinaryFileName, FileMode.Create)))
        //    {
        //        DirectoryInfo dirIn = new DirectoryInfo(srcTextFileFolderName);
        //        FileInfo[] files = dirIn.GetFiles();
        //        foreach (FileInfo fileInfo in files)
        //        {
        //            using (StreamReader reader = new StreamReader(fileInfo.FullName))
        //            {
        //                string line;

        //                while ((line = reader.ReadLine()) != null)
        //                {
        //                    string[] subString = line.Split(',');
        //                    int origin_zone = Int32.Parse(subString[1]);
        //                    int dest_zone = Int32.Parse(subString[2]);

        //                    if (origin_zone == 0 || dest_zone == 0)
        //                    {
        //                        continue;
        //                    }

        //                    double travel_time = Double.Parse(subString[3]) / 60;
        //                    double start_time = Double.Parse(subString[4]);
        //                    int num_of_links = Int32.Parse(subString[5]);

        //                    binWriter.Write(n);
        //                    binWriter.Write(origin_zone);
        //                    binWriter.Write(dest_zone);
        //                    binWriter.Write(start_time);
        //                    binWriter.Write(travel_time);
        //                    binWriter.Write(num_of_links);

        //                    for (int i = 0; i < num_of_links; i++)
        //                    {
        //                        double x1 = double.Parse(subString[6 + 6 * i + 0]);
        //                        double y1 = double.Parse(subString[6 + 6 * i + 1]);
        //                        double x2 = double.Parse(subString[6 + 6 * i + 2]);
        //                        double y2 = double.Parse(subString[6 + 6 * i + 3]);
        //                        double link_start_time = double.Parse(subString[6 + 6 * i + 4]);
        //                        double link_travel_time = double.Parse(subString[6 + 6 * i + 5]);

        //                        binWriter.Write(x1);
        //                        binWriter.Write(y1);
        //                        binWriter.Write(x2);
        //                        binWriter.Write(y2);
        //                        //binWriter.Write(link_start_time);
        //                        //binWriter.Write(link_travel_time);
        //                    }
        //                    n++;
        //                }
        //            }
        //        }
        //    }
        //}
    }

    public enum VehicleTrajectoryType
    {
        DYNASMART,
        AIMSUN,
        GPS
    }

    public interface IVehicle
    {
        //property
        VehicleTrajectoryType TrajectoryType
        {
            get;
        }

        int VehicleID
        {
            get;
        }
        int OriginZoneID
        {
            get;
        }
        int DestinationZoneID
        {
            get;
        }
        DateTime VehicleStartTime
        {
            get;
        }
        int VehicleStartHour
        {
            get;
        }
        float VehicleTravelTime
        {
            get;
        }

        List<int> NodeList
        {
            get;
        }

        float Travel_Distance_In_Mile
        {
            get;
        }

        //Methods
        string ConvertVehicleTraceToPlacemark(string name, string colorCode);
        GPSLink[] GetGPSLinkArray();
        //string ConvertVehicleTraceToKMLString(string name, string colorCode);
    }
}
